package common.messages;

import java.util.ArrayList;
import java.util.List;

public class KVMessageImpl implements KVMessage {
	private String key;
	private String value;
	private StatusType status;
	private List<MetaData> metadata;

	private static byte[] msgBytes;
	private static final char LINE_FEED = 0x0A;
	private static final char RETURN = 0x0D;
	private static final String CLIENT_TAG = "1";

	/**
	 * @param k
	 *            key
	 * @param v
	 *            value
	 * @param s
	 *            message
	 * @param m
	 *            metadata
	 */
	public KVMessageImpl(String k, String v, String s, String m) {
		this.key = k;
		this.value = v;
		this.status = StatusType.valueOf(s.toUpperCase());
		// unmarshal string to metadata
		this.metadata = new ArrayList<MetaData>();
		if (!m.isEmpty()) {
			this.metadata = string2MetaData(m);
		}
	}

	@Override
	public String getKey() {
		return key;
	}

	@Override
	public String getValue() {
		return value;
	}

	@Override
	public StatusType getStatus() {
		return status;
	}

	@Override
	public List<MetaData> getMetadata() {
		return metadata;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public void setStatus(StatusType status) {
		this.status = status;
	}

	public void setMetadata(List<MetaData> metadata) {
		this.metadata = metadata;
	}

	/**
	 * @param km
	 *            input a KVmessage
	 * @return the byte array
	 */
	public byte[] toByteArray(KVMessageImpl km) {
		String meta = metadata2String(km.getMetadata());
		String s = CLIENT_TAG + "," + km.getKey() + "," + km.getValue() + ","
				+ km.getStatus().toString() + "," + meta;
		byte[] bytes = s.getBytes();
		byte[] ctrBytes = new byte[] { LINE_FEED, RETURN };
		byte[] tmp = new byte[bytes.length + ctrBytes.length];

		System.arraycopy(bytes, 0, tmp, 0, bytes.length);
		System.arraycopy(ctrBytes, 0, tmp, bytes.length, ctrBytes.length);

		return tmp;
	}

	/**
	 * 
	 * @param bytes
	 *            the byte array
	 * @return the KVmessage
	 */
	private static byte[] addCtrChars(byte[] bytes) {
		byte[] ctrBytes = new byte[] { LINE_FEED, RETURN };
		byte[] tmp = new byte[bytes.length + ctrBytes.length];
		System.arraycopy(bytes, 0, tmp, 0, bytes.length);
		System.arraycopy(ctrBytes, 0, tmp, bytes.length, ctrBytes.length);

		return tmp;
	}

	/**
	 * Unmarshal the byte array to KVMessage
	 * 
	 * @param bytes
	 * @return KVMessage
	 */
	public static KVMessageImpl toKVMessage(byte[] bytes) {
		msgBytes = addCtrChars(bytes);
		String str = new String(msgBytes).trim();
		String[] temps = str.split(",");
		if (temps.length > 4) {
			return new KVMessageImpl(temps[1], temps[2],
					temps[3].toUpperCase(), temps[4]);
		} else {
			return new KVMessageImpl(temps[1], temps[2],
					temps[3].toUpperCase(), "");
		}
	}

	/**
	 * transform the list of metadata to string
	 * 
	 * @param mList
	 * @return string
	 */
	private String metadata2String(List<MetaData> mList) {
		String meta = "";
		for (MetaData m : mList) {
			meta += m.getIp() + ":" + m.getPort() + ":" + m.getHead() + ":"
					+ m.getTail() + ";";
		}
		return meta;
	}

	/**
	 * transform the string to list of metadata
	 * 
	 * @param meta
	 * @return List of metadata
	 */
	private List<MetaData> string2MetaData(String meta) {
		List<MetaData> mList = new ArrayList<MetaData>();
		String[] listSpilit = meta.split(";");
		for (String s : listSpilit) {
			String[] metaSpilit = s.split(":");
			MetaData m = new MetaData(metaSpilit[0], metaSpilit[1],
					metaSpilit[2], metaSpilit[3]);
			mList.add(m);
		}
		return mList;
	}
}
